package com.mirror.tk.api.utils;

import java.security.Key;
import java.util.Calendar;
import java.util.Set;

import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import com.mirror.tk.api.common.support.JwtUser;
import com.mirror.tk.core.module.user.domain.User;
import com.mirror.tk.framework.utils.Reflections;

/**
 * jwt工具类
 */
public class JwtUtil {
	
	private static RsaJsonWebKey rsaJsonWebKey = null;				// 加密key 创建一个加密秘钥（应该全局共享）可以使用JWK代替
	private static final String KEY_ID = "mirror_key";				//key id
	
	private static final String ISSUER = "api.mirror.cn";				//颁发者
	private static final String AUDIENCE = "www.mirror.cn";			//被授权者
	private static final int EXPIRATION_TIME = 24 * 60 * 7;				//过期时间
	private static final int MAX_FUTURE_VALIDITY_IN_MINUTES = 60 * 24 * 14;//生效时间
	private static final int ALLOWED_CLOCK_SKEW_IN_SECONDS = 24 * 60;	//过期认证时间
	private static final int VALID_TIME = 60 * 30 * 1000;			//有效时间（30分钟）
	
	// 该地方需要重写为方法
	static {
		try {
			rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
		} catch (JoseException e) {
			e.printStackTrace();
		}
		rsaJsonWebKey.setKeyId(KEY_ID);
	}
	
	/**
	 * 创建jwt
	 * <p>Description:根据用户信息创建jwt</p>
	 * @param user
	 * @return
	 * @throws JoseException
	 * @author fengshi on 2016年12月1日
	 */
	public static String createJwt(JwtUser user) throws JoseException{
		JwtClaims claims = getJwtClaims(ISSUER, user.getPhone(), AUDIENCE, EXPIRATION_TIME);
		Set<String> fields = Reflections.getFieldNames(JwtUser.class);
		for (String field : fields) {
			claims.setClaim(field, Reflections.getFieldValue(user, field));
		}
		JsonWebSignature jws = getJws(rsaJsonWebKey);
		jws.setPayload(claims.toJson());
		return jws.getCompactSerialization();
	}
	
	/**
	 * 验证jwt
	 * <p>Description:验证jwt是否合法</p>
	 * @param jwt
	 * @return
	 * @author fengshi on 2016年12月1日
	 */
	public static Boolean validateJwt(String jwt){
		return validateJwt(jwt, ALLOWED_CLOCK_SKEW_IN_SECONDS, MAX_FUTURE_VALIDITY_IN_MINUTES, ISSUER, AUDIENCE, rsaJsonWebKey.getKey());
	}
	
	/**
	 * 验证jwt
	 * <p>Description:验证jwt是否合法</p>
	 * @param jwt
	 * @param secondsOfAllowedClockSkew
	 * @param maxFutureValidityInMinutes
	 * @param expectedIssuer
	 * @param audience
	 * @param verificationKey
	 * @return
	 * @author fengshi on 2016年12月1日
	 */
	public static Boolean validateJwt(String jwt, Integer secondsOfAllowedClockSkew, Integer maxFutureValidityInMinutes, String expectedIssuer, String audience, Key verificationKey){
		boolean flag = true;
		JwtConsumer jwtConsumer = new JwtConsumerBuilder()
				.setRequireSubject()
				.setRequireExpirationTime()
				.setAllowedClockSkewInSeconds(secondsOfAllowedClockSkew)
				.setMaxFutureValidityInMinutes(maxFutureValidityInMinutes)
				.setExpectedIssuer(expectedIssuer)
				.setExpectedAudience(audience)
				.setVerificationKey(verificationKey)
				.build();
		try {
			jwtConsumer.process(jwt);
		} catch (InvalidJwtException e) {
			e.printStackTrace();
			flag = false;
		}
		return flag;
	}
	
	/**
	 * 获取jwt里面的数据
	 * <p>Description:获取jwt里面的数据</p>
	 * @param jwt
	 * @return
	 * @throws InvalidJwtException
	 * @author fengshi on 2016年12月1日
	 */
	public static JwtClaims getClaimsByJwt(String jwt) throws InvalidJwtException{
		JwtConsumer jwtConsumer = new JwtConsumerBuilder()
				.setRequireSubject()
				.setRequireExpirationTime()
				.setAllowedClockSkewInSeconds(ALLOWED_CLOCK_SKEW_IN_SECONDS)
				.setMaxFutureValidityInMinutes(MAX_FUTURE_VALIDITY_IN_MINUTES)
				.setExpectedIssuer(ISSUER)
				.setExpectedAudience(AUDIENCE)
				.setVerificationKey(rsaJsonWebKey.getKey())
				.build();
		return jwtConsumer.processToClaims(jwt);
	}
	
	/**
	 * 获取jwt里面的用户信息
	 * <p>Description:获取jwt里面的用户信息</p>
	 * @param jwt
	 * @return
	 * @author fengshi on 2016年12月1日
	 */
	public static JwtUser getJwtUser(String jwt) {
		try {
			return getJwtUser(getClaimsByJwt(jwt));
		} catch (InvalidJwtException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取jwt里面的用户信息
	 * <p>Description:获取jwt里面的用户信息</p>
	 * @param claims
	 * @return
	 * @author fengshi on 2016年12月1日
	 */
	public static JwtUser getJwtUser(JwtClaims claims){
		User user = new User();
		user.setId((Long)claims.getClaimValue("id"));
		user.setUsername((String)claims.getClaimValue("username"));
		user.setPhone((String)claims.getClaimValue("phone"));
		user.setQq((String)claims.getClaimValue("qq"));
		user.setWechat(((String)claims.getClaimValue("wechat")));
		user.setEmail((String)claims.getClaimValue("email"));
		user.setScore((Long)claims.getClaimValue("score"));
		if(null != claims.getClaimValue("status")){
			user.setStatus(Integer.valueOf(claims.getClaimValue("status").toString()));
		}
		if(null != claims.getClaimValue("age")){
			user.setAge(Integer.valueOf(claims.getClaimValue("age").toString()));
		}
		if(null != claims.getClaimValue("sex")){
			user.setSex(Integer.valueOf((String)claims.getClaimValue("sex").toString()));
		}
		return JwtUser.bulider(user);
	}
	
	public static JwtClaims getJwtClaims(String issuer, String subject, String audience, int expirationTime){
		JwtClaims claims = new JwtClaims();
		claims.setIssuer(issuer);
		claims.setSubject(subject);
		claims.setAudience(audience);
		claims.setExpirationTimeMinutesInTheFuture(expirationTime);
		claims.setIssuedAtToNow();
		claims.setGeneratedJwtId();
		return claims;
	}
	
	public static JsonWebSignature getJws(RsaJsonWebKey rsaJwk){
		JsonWebSignature jws = new JsonWebSignature();
		jws.setKey(rsaJwk.getPrivateKey());
		jws.setKeyIdHeaderValue(rsaJwk.getKeyId());
		jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
		return jws;
	}

	/**
	 * 判断jwt是否需要刷新
	 * <p>Description:如果过期时间小于指定数值则需要刷新</p>
	 * @param jwt
	 * @return
	 * @author fengshi on 2016年12月1日
	 * @throws MalformedClaimException 
	 * @throws InvalidJwtException 
	 */
	public static Boolean isOverdue(String jwt) throws MalformedClaimException, InvalidJwtException {
		NumericDate expTime = getClaimsByJwt(jwt).getExpirationTime();
		return expTime.getValue() + VALID_TIME < Calendar.getInstance().getTimeInMillis();
	}

}
